
package com.jf.cloud.order.controller.app;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.jf.cloud.api.combo.feign.ComboFeignClient;
import com.jf.cloud.api.coupon.dto.ChooseCouponDTO;
import com.jf.cloud.api.coupon.dto.PlatformChooseCouponDTO;
import com.jf.cloud.api.coupon.feign.CouponOrderFeignClient;
import com.jf.cloud.api.delivery.dto.CalculateAndGetDeliverInfoDTO;
import com.jf.cloud.api.delivery.feign.DeliveryFeignClient;
import com.jf.cloud.api.discount.feign.DiscountFeignClient;
import com.jf.cloud.api.order.manager.ConfirmOrderManager;
import com.jf.cloud.api.order.manager.SubmitOrderManager;
import com.jf.cloud.api.product.manager.ShopCartAdapter;
import com.jf.cloud.api.product.manager.ShopCartItemAdapter;
import com.jf.cloud.api.user.feign.UserLevelAndScoreOrderFeignClient;
import com.jf.cloud.common.cache.constant.CacheNames;
import com.jf.cloud.common.cache.util.RedisUtil;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.order.constant.DeliveryType;
import com.jf.cloud.common.order.constant.OrderType;
import com.jf.cloud.common.order.dto.OrderDTO;
import com.jf.cloud.common.order.dto.SubmitOrderDTO;
import com.jf.cloud.common.order.util.OrderLangUtil;
import com.jf.cloud.common.order.vo.*;
import com.jf.cloud.common.product.constant.PreSaleType;
import com.jf.cloud.common.response.ResponseEnum;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.rocketmq.config.RocketMqConstant;
import com.jf.cloud.common.security.AuthUserContext;
import com.jf.cloud.order.bo.SubmitOrderPayAmountInfoBO;
import com.jf.cloud.order.model.OrderAddr;
import com.jf.cloud.order.service.OrderAddrService;
import com.jf.cloud.order.service.OrderItemService;
import com.jf.cloud.order.service.OrderService;
import com.jf.cloud.order.vo.SubmitOrderPayInfoVO;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 订单信息
 *
 * @author zz
 * @date 2020-12-05 14:13:50
 */
@RestController("appOrderController")
@RequestMapping("/order")
@Tag(name = "app-订单信息")
public class OrderController {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);

    @Autowired
    private OrderService orderService;

    @Autowired
    private ShopCartAdapter shopCartAdapter;

    @Autowired
    private ConfirmOrderManager confirmOrderManager;

    @Autowired
    private ShopCartItemAdapter shopCartItemAdapter;

    @Autowired
    private DiscountFeignClient discountFeignClient;

    @Autowired
    private ThreadPoolExecutor orderThreadPoolExecutor;

    @Autowired
    private DeliveryFeignClient deliveryFeignClient;

    @Autowired
    private CouponOrderFeignClient couponOrderFeignClient;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private OrderAddrService orderAddrService;

    @Autowired
    private SubmitOrderManager submitOrderManager;

    @Autowired
    private UserLevelAndScoreOrderFeignClient userLevelAndScoreOrderFeignClient;

    @Autowired
    private ComboFeignClient comboFeignClient;


    /**
     * 生成订单
     */
    @PostMapping("/confirm")
    @Operation(summary = "结算，生成订单信息" , description = "传入下单所需要的参数进行下单")
    public ServerResponseEntity<ShopCartOrderMergerVO> confirm(@Valid @RequestBody OrderDTO orderParam) throws ExecutionException, InterruptedException {
        Long userId = AuthUserContext.get().getUserId();
        // 将要返回给前端的完整的订单信息
        ShopCartOrderMergerVO shopCartOrderMerger = getShopCartOrderMergerVO(orderParam);
        List<ShopCartItemVO> shopCartItemsDb = shopCartItemAdapter.getShopCartItems(orderParam.getShopCartItem(), userId, orderParam.getAddrId());
        // 检查地址
        confirmOrderManager.checkAddr(shopCartItemsDb, userId, orderParam.getAddrId());
        // 筛选过滤掉不同配送的商品
        List<ShopCartItemVO> shopCartItems = confirmOrderManager.filterShopItemsByType(shopCartOrderMerger, shopCartItemsDb);
        setMold(shopCartOrderMerger, shopCartItemsDb);

        setPreSaleType(shopCartOrderMerger, shopCartItems);

        OrderLangUtil.shopCartItemList(shopCartItems);

        // 购物车
        List<ShopCartVO> shopCarts = shopCartAdapter.conversionShopCart(shopCartItems);

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        // 异步计算运费，运费暂时和优惠券没啥关联，可以与优惠券异步计算，获取用户地址，自提信息
        CompletableFuture<ServerResponseEntity<UserDeliveryInfoVO>> deliveryFuture = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            return deliveryFeignClient.calculateAndGetDeliverInfo(new CalculateAndGetDeliverInfoDTO(orderParam.getAddrId(), orderParam.getStationId(), shopCartItems, orderParam.getDvyTypes()));
        }, orderThreadPoolExecutor);


        // 计算满减，并重新组合购物车 (满减是从购物车的时候就已经计算好了，提交订单理所当然应该用相同的方法)
        ServerResponseEntity<List<ShopCartVO>> discountShopCartsResponseEntity = discountFeignClient.calculateDiscountAndMakeUpShopCart(shopCarts);
        if (!discountShopCartsResponseEntity.isSuccess()) {
            return ServerResponseEntity.transform(discountShopCartsResponseEntity);
        }
        shopCarts = discountShopCartsResponseEntity.getData();


        // 计算优惠券，并返回优惠券信息
        ServerResponseEntity<List<ShopCartVO>> couponShopCartsResponseEntity = couponOrderFeignClient.chooseShopCoupon(new ChooseCouponDTO(orderParam.getUserChangeCoupon(), orderParam.getCouponIds(), shopCarts));
        if (!couponShopCartsResponseEntity.isSuccess()) {
            LOGGER.info("优惠券计算失败，失败原因：{}", couponShopCartsResponseEntity.getMsg());
            return ServerResponseEntity.transform(couponShopCartsResponseEntity);
        }
        shopCarts = couponShopCartsResponseEntity.getData();

        // 运费用异步计算，最后要等运费出结果
        ServerResponseEntity<UserDeliveryInfoVO> userDeliveryInfoResponseEntity = deliveryFuture.get();
        if (!userDeliveryInfoResponseEntity.isSuccess()) {
            LOGGER.info("运费计算失败，失败原因：{}", userDeliveryInfoResponseEntity.getMsg());
            return ServerResponseEntity.transform(userDeliveryInfoResponseEntity);
        }
        // 当算完一遍店铺的各种满减活动时，重算一遍订单金额
        confirmOrderManager.recalculateAmountWhenFinishingCalculateShop(shopCartOrderMerger, shopCarts, userDeliveryInfoResponseEntity.getData());
        long orderShopReduce = shopCartOrderMerger.getOrderReduce();

        // ===============================================开始平台优惠的计算==================================================
        // 计算平台优惠券，并返回平台优惠券信息
        ServerResponseEntity<ShopCartOrderMergerVO> couponOrderMergerResponseEntity = couponOrderFeignClient.choosePlatformCoupon(new PlatformChooseCouponDTO(orderParam.getUserChangeCoupon(), orderParam.getCouponIds(), shopCartOrderMerger));

        if (!couponOrderMergerResponseEntity.isSuccess()) {
            return ServerResponseEntity.transform(couponOrderMergerResponseEntity);
        }

        shopCartOrderMerger = couponOrderMergerResponseEntity.getData();
        ServerResponseEntity<ShopCartOrderMergerVO> calculateLevelDiscountResponseEntity = userLevelAndScoreOrderFeignClient.calculateLevelAndScoreDiscount(shopCartOrderMerger);
        if (!calculateLevelDiscountResponseEntity.isSuccess()) {
            LOGGER.info("计算会员等级优惠失败，失败原因：{}", calculateLevelDiscountResponseEntity.getMsg());
            return ServerResponseEntity.transform(calculateLevelDiscountResponseEntity);
        }
        shopCartOrderMerger = calculateLevelDiscountResponseEntity.getData();

        // ===============================================结束平台优惠的计算==================================================

        // 结束平台优惠的计算之后，还要重算一遍金额
        confirmOrderManager.recalculateAmountWhenFinishingCalculatePlatform(shopCartOrderMerger, userDeliveryInfoResponseEntity.getData());

        // 重新插入spu、sku
        resetSpu(shopCartOrderMerger, shopCartItemsDb);
        shopCartOrderMerger.setOrderShopReduce(orderShopReduce);
        // 计算平台佣金-跟订单其他金额是独立的，可以单独计算
        confirmOrderManager.confirmPlatformCommission(shopCartOrderMerger);
        // 缓存计算新
        confirmOrderManager.cacheCalculatedInfo(userId, shopCartOrderMerger);

        // 无法配送的商品添加商品详细和店铺详细
        shopCartAdapter.setFilterShopItemsLong(shopCartOrderMerger);
        return ServerResponseEntity.success(shopCartOrderMerger);
    }

    private static ShopCartOrderMergerVO getShopCartOrderMergerVO(OrderDTO orderParam) {
        ShopCartOrderMergerVO shopCartOrderMerger = new ShopCartOrderMergerVO();
        shopCartOrderMerger.setIsScorePay(orderParam.getIsScorePay());
        shopCartOrderMerger.setDvyTypes(orderParam.getDvyTypes());
        shopCartOrderMerger.setUsableScore(orderParam.getUserUseScore());
        shopCartOrderMerger.setOrderType(OrderType.ORDINARY);
        return shopCartOrderMerger;
    }
    /**
     * 购物车/立即购买  提交订单,根据店铺拆单
     */
    @PostMapping("/submit")
    @Operation(summary = "提交订单，返回支付流水号" , description = "根据传入的参数判断是否为购物车提交订单，同时对购物车进行删除，用户开始进行支付")
    public ServerResponseEntity<List<Long>> submitOrders(@Valid @RequestBody SubmitOrderDTO submitOrderParam) {
        Long userId = AuthUserContext.get().getUserId();

        // 在这里面已经生成了订单id了
        ServerResponseEntity<ShopCartOrderMergerVO> checkResult = submitOrderManager.checkSubmitInfo(submitOrderParam, userId);
        if (!checkResult.isSuccess()) {
            LOGGER.info("提交订单失败，失败原因:{}", checkResult.getMsg());
            return ServerResponseEntity.transform(checkResult);
        }
        ShopCartOrderMergerVO mergerOrder = checkResult.getData();

        List<ShopCartOrderVO> shopCartOrders = mergerOrder.getShopCartOrders();

        // 判断套餐是否失效
        List<Long> comboIdList = new ArrayList<>();
        for (ShopCartOrderVO shopCartOrder : shopCartOrders) {
            List<ShopCartItemDiscountVO> shopCartItemDiscounts = shopCartOrder.getShopCartItemDiscounts();
            for (ShopCartItemDiscountVO shopCartItemDiscount : shopCartItemDiscounts) {
                for (ShopCartItemVO shopCartItem : shopCartItemDiscount.getShopCartItems()) {
                    if (Objects.isNull(shopCartItem.getComboId())) {
                        continue;
                    }
                    Integer comboStatus = RedisUtil.get(CacheNames.ORDER_COMBO_STATUS + shopCartItem.getComboId());
                    Long comboExpirationTime = RedisUtil.get(CacheNames.ORDER_COMBO_EXPIRATION_TIME + shopCartItem.getComboId());
                    if (!Objects.equals(comboStatus, 1)) {
                        LOGGER.info("套餐状态不可用，comboId:{}", shopCartItem.getComboId());
                        throw new LuckException("当前选择的套餐活动已经过期，请返回重新提交订单");
                    }
                    if (comboExpirationTime < System.currentTimeMillis()) {
                        LOGGER.info("套餐已过期，comboId:{}", shopCartItem.getComboId());
                        throw new LuckException("当前选择的套餐活动已经过期，请返回重新提交订单");
                    }
                }
            }
        }
        LOGGER.info("购物车:{}", shopCartOrders.toString());
        // 锁定库存
        submitOrderManager.tryLockStock(shopCartOrders);

        if (Objects.equals(mergerOrder.getOrderType(), OrderType.ORDINARY)) {
            LOGGER.info("提交普通订单，锁定优惠券");
            // 锁定优惠券
            submitOrderManager.tryLockCoupon(mergerOrder);
        }

        // 锁积分
        submitOrderManager.tryLockScore(mergerOrder);

        mergerOrder.setUserId(userId);
        // 提交订单
        List<Long> orderIds = orderService.submit(mergerOrder);

        return ServerResponseEntity.success(orderIds);
    }

    @GetMapping("/order_pay_info")
    @Operation(summary = "获取订单支付信息" , description = "获取订单支付的商品/地址信息")
    @Parameter(name = "orderIds", description = "订单流水号" , required = true)
    public ServerResponseEntity<SubmitOrderPayInfoVO> getOrderPayInfo(@ParameterObject @RequestParam("orderIds") String orderIds) {
        long[] orderIdList = StrUtil.splitToLong(orderIds, ",");
        List<String> spuNameList = orderItemService.getSpuNameListByOrderIds(orderIdList);

        //获取订单信息
        SubmitOrderPayAmountInfoBO submitOrderPayAmountInfo = orderService.getSubmitOrderPayAmountInfo(orderIdList);

        if (Objects.isNull(submitOrderPayAmountInfo) || Objects.isNull(submitOrderPayAmountInfo.getCreateTime()) ) {
            LOGGER.error("订单不存在，订单号:{}", orderIds);
            return ServerResponseEntity.fail(ResponseEnum.ORDER_NOT_EXIST);
        }

        Date endTime =  DateUtil.offsetMillisecond(submitOrderPayAmountInfo.getCreateTime(), RocketMqConstant.CANCEL_TIME_INTERVAL);
        SubmitOrderPayInfoVO orderPayInfoParam = new SubmitOrderPayInfoVO();
        orderPayInfoParam.setSpuNameList(spuNameList);
        orderPayInfoParam.setEndTime(endTime);
        orderPayInfoParam.setTotalFee(submitOrderPayAmountInfo.getTotalFee());
        orderPayInfoParam.setTotalScore(submitOrderPayAmountInfo.getTotalScore());
        orderPayInfoParam.setOrderScore(submitOrderPayAmountInfo.getOrderScore());
        orderPayInfoParam.setOrderType(submitOrderPayAmountInfo.getOrderType());
        // 如果是定金预售，判断下是支付尾款还是定金
        if(Objects.equals(submitOrderPayAmountInfo.getPreSaleType(), PreSaleType.DEPOSIT.value())){
            if(Objects.equals(submitOrderPayAmountInfo.getPreSalePayStatus(),0)){
                orderPayInfoParam.setTotalFee(submitOrderPayAmountInfo.getDepositAmount());
            }else{
                orderPayInfoParam.setTotalFee(submitOrderPayAmountInfo.getBalanceAmount() + submitOrderPayAmountInfo.getFreightAmount() - submitOrderPayAmountInfo.getPlatformFreeFreightAmount());
                // 支付尾款的话，修改下剩余支付时间
                endTime =  DateUtil.offsetMillisecond(submitOrderPayAmountInfo.getBalanceEndTime(), RocketMqConstant.CANCEL_TIME_INTERVAL);
                orderPayInfoParam.setEndTime(endTime);
                orderPayInfoParam.setPreSalePayStatus(submitOrderPayAmountInfo.getPreSalePayStatus());
                orderPayInfoParam.setBalanceStartTime(submitOrderPayAmountInfo.getBalanceStartTime());
            }
        }
        // 地址
        if (Objects.nonNull(submitOrderPayAmountInfo.getOrderAddrId())) {
            OrderAddr orderAddr = orderAddrService.getById(submitOrderPayAmountInfo.getOrderAddrId());
            LOGGER.info("订单地址信息:{}", orderAddr);
            if(Objects.nonNull(orderAddr)){
                //写入商品名、收货地址/电话
                String addr = orderAddr.getProvince() + orderAddr.getCity() + orderAddr.getArea() + orderAddr.getAddr();
                orderPayInfoParam.setUserAddr(addr);
                orderPayInfoParam.setConsignee(orderAddr.getConsignee());
                orderPayInfoParam.setMobile(orderAddr.getMobile());
            }
        }
        return ServerResponseEntity.success(orderPayInfoParam);
    }

    @GetMapping("/create_order_status")
    @Operation(summary = "根据订单id获取订单是否创建成功" , description = "订单数量")
    @Parameter(name = "orderId", description = "订单流水号" , required = true)
    public ServerResponseEntity<Integer> getCreateOrderStatus(@ParameterObject @RequestParam("orderId") Long orderId) {
        return ServerResponseEntity.success(orderService.countByOrderId(orderId));
    }

    private static void setPreSaleType(ShopCartOrderMergerVO shopCartOrderMerger, List<ShopCartItemVO> shopCartItems) {
        // 该商品不满足任何的配送方式
        if (CollectionUtil.isEmpty(shopCartItems)) {
            LOGGER.info("该商品不满足任何的配送方式,并设置配送方式为-1");
            shopCartOrderMerger.setPreSaleType(-1);
        } else {
            // 判断下预售
            ShopCartItemVO firstShopCartItem = shopCartItems.get(0);
            // 是否为预售订单
            shopCartOrderMerger.setPreSaleType(firstShopCartItem.getPreSaleType());
            LOGGER.info("是否为预售订单:{}", shopCartOrderMerger.getPreSaleType());
        }
    }

    private static void resetSpu(ShopCartOrderMergerVO shopCartOrderMerger, List<ShopCartItemVO> shopCartItemsDb) {
        Map<Long, ShopCartItemVO> shopCartItemMap = shopCartItemsDb.stream().collect(Collectors.toMap(ShopCartItemVO::getSkuId, s -> s, (s1, s2) -> s1));
        for (ShopCartOrderVO shopCartOrder : shopCartOrderMerger.getShopCartOrders()) {
            for (ShopCartItemDiscountVO shopCartItemDiscount : shopCartOrder.getShopCartItemDiscounts()) {
                for (ShopCartItemVO shopCartItem : shopCartItemDiscount.getShopCartItems()) {
                    ShopCartItemVO shopCartItemVO = shopCartItemMap.get(shopCartItem.getSkuId());
                    shopCartItem.setSkuLangList(shopCartItemVO.getSkuLangList());
                    shopCartItem.setSpuLangList(shopCartItemVO.getSpuLangList());
                }
            }
        }
    }

    private static void setMold(ShopCartOrderMergerVO shopCartOrderMerger, List<ShopCartItemVO> shopCartItemsDb) {
        // 商品类别 0.实物商品 1. 虚拟商品
        int mold = 0;
        if (shopCartItemsDb.stream().filter(shopCartItemDto -> Objects.nonNull(shopCartItemDto.getSpuMold()) && shopCartItemDto.getSpuMold() == 1).count() == shopCartItemsDb.size()) {
            LOGGER.info("订单项中的所有商品都为虚拟商品时，才是虚拟订单,并设置配送方式为不配送");
            // 订单项中的所有商品都为虚拟商品时，才是虚拟订单
            mold = 1;
            shopCartOrderMerger.getDvyTypes().forEach(s -> s.setDvyType(DeliveryType.NOT_DELIVERY.value()));
        }
        shopCartOrderMerger.setSpuMold(mold);
    }
}
